home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_300 / 365_02 / system.c < prev    next >
C/C++ Source or Header  |  1992-04-04  |  9KB  |  424 lines

  1. /* system.c  -- UNIX version */
  2.  
  3. /* Author:
  4.  *    Steve Kirkendall
  5.  *    14407 SW Teal Blvd. #C
  6.  *    Beaverton, OR 97005
  7.  *    kirkenda@cs.pdx.edu
  8.  */
  9.  
  10.  
  11. /* This file contains a new version of the system() function and related stuff.
  12.  *
  13.  * Entry points are:
  14.  *    system(cmd)        - run a single shell command
  15.  *    wildcard(names)        - expand wildcard characters in filanames
  16.  *    filter(m,n,cmd,back)    - run text lines through a filter program
  17.  *
  18.  * This is probably the single least portable file in the program.  The code
  19.  * shown here should work correctly if it links at all; it will work on UNIX
  20.  * and any O.S./Compiler combination which adheres to UNIX forking conventions.
  21.  */
  22.  
  23. #include "config.h"
  24. #include "vi.h"
  25. extern char    **environ;
  26.  
  27. #if ANY_UNIX
  28.  
  29. /* This is a new version of the system() function.  The only difference
  30.  * between this one and the library one is: this one uses the o_shell option.
  31.  */
  32. int system(cmd)
  33.     char    *cmd;    /* a command to run */
  34. {
  35.     int    pid;    /* process ID of child */
  36.     int    died;
  37.     int    status;    /* exit status of the command */
  38.  
  39.  
  40.     signal(SIGINT, SIG_IGN);
  41.     pid = fork();
  42.     switch (pid)
  43.     {
  44.       case -1:                        /* error */
  45.         msg("fork() failed");
  46.         status = -1;
  47.         break;
  48.  
  49.       case 0:                        /* child */
  50.         /* for the child, close all files except stdin/out/err */
  51.         for (status = 3; status < 60 && (close(status), errno != EINVAL); status++)
  52.         {
  53.         }
  54.  
  55.         signal(SIGINT, SIG_DFL);
  56.         if (cmd == o_shell)
  57.         {
  58.             execle(o_shell, o_shell, (char *)0, environ);
  59.         }
  60.         else
  61.         {
  62.             execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
  63.         }
  64.         msg("execle(\"%s\", ...) failed", o_shell);
  65.         exit(1); /* if we get here, the exec failed */
  66.  
  67.       default:                        /* parent */
  68.         do
  69.         {
  70.             died = wait(&status);
  71.         } while (died >= 0 && died != pid);
  72.         if (died < 0)
  73.         {
  74.             status = -1;
  75.         }
  76. #if __GNUC__
  77.         signal(SIGINT, (void (*)()) trapint);
  78. #else
  79.         signal(SIGINT, trapint);
  80. #endif
  81.     }
  82.  
  83.     return status;
  84. }
  85.  
  86. /* This private function opens a pipe from a filter.  It is similar to the
  87.  * system() function above, and to popen(cmd, "r").
  88.  */
  89. int rpipe(cmd, in)
  90.     char    *cmd;    /* the filter command to use */
  91.     int    in;    /* the fd to use for stdin */
  92. {
  93.     int    r0w1[2];/* the pipe fd's */
  94.  
  95.     /* make the pipe */
  96.     if (pipe(r0w1) < 0)
  97.     {
  98.         return -1;    /* pipe failed */
  99.     }
  100.  
  101.     /* The parent process (elvis) ignores signals while the filter runs.
  102.      * The child (the filter program) will reset this, so that it can
  103.      * catch the signal.
  104.      */
  105.     signal(SIGINT, SIG_IGN);
  106.  
  107.     switch (fork())
  108.     {
  109.       case -1:                        /* error */
  110.         return -1;
  111.  
  112.       case 0:                        /* child */
  113.         /* close the "read" end of the pipe */
  114.         close(r0w1[0]);
  115.  
  116.         /* redirect stdout to go to the "write" end of the pipe */
  117.         close(1);
  118.         dup(r0w1[1]);
  119.         close(2);
  120.         dup(r0w1[1]);
  121.         close(r0w1[1]);
  122.  
  123.         /* redirect stdin */
  124.         if (in != 0)
  125.         {
  126.             close(0);
  127.             dup(in);
  128.             close(in);
  129.         }
  130.  
  131.         /* the filter should accept SIGINT signals */
  132.         signal(SIGINT, SIG_DFL);
  133.  
  134.         /* exec the shell to run the command */
  135.         execle(o_shell, o_shell, "-c", cmd, (char *)0, environ);
  136.         exit(1); /* if we get here, exec failed */
  137.  
  138.       default:                        /* parent */
  139.         /* close the "write" end of the pipe */    
  140.         close(r0w1[1]);
  141.  
  142.         return r0w1[0];
  143.     }
  144. }
  145.  
  146. #endif /* non-DOS */
  147.  
  148. #if OSK
  149.  
  150. /* This private function opens a pipe from a filter.  It is similar to the
  151.  * system() function above, and to popen(cmd, "r").
  152.  */
  153. int rpipe(cmd, in)
  154.     char    *cmd;    /* the filter command to use */
  155.     int    in;    /* the fd to use for stdin */
  156. {
  157.     return osk_popen(cmd, "r", in, 0);
  158. }    
  159. #endif
  160.  
  161. #if ANY_UNIX || OSK
  162.  
  163. /* This function closes the pipe opened by rpipe(), and returns 0 for success */
  164. int rpclose(fd)
  165.     int    fd;
  166. {
  167.     int    status;
  168.  
  169.     close(fd);
  170.     wait(&status);
  171. #if __GNUC__
  172.     signal(SIGINT, (void (*)()) trapint);
  173. #else
  174.     signal(SIGINT, trapint);
  175. #endif
  176.     return status;
  177. }
  178.  
  179. #endif /* non-DOS */
  180.  
  181. /* This function expands wildcards in a filename or filenames.  It does this
  182.  * by running the "echo" command on the filenames via the shell; it is assumed
  183.  * that the shell will expand the names for you.  If for any reason it can't
  184.  * run echo, then it returns the names unmodified.
  185.  */
  186.  
  187. #if MSDOS || TOS
  188. #define    PROG    "wildcard "
  189. #define    PROGLEN    9
  190. #include <string.h>
  191. #else
  192. #define    PROG    "echo "
  193. #define    PROGLEN    5
  194. #endif
  195.  
  196. #if !AMIGA
  197. char *wildcard(names)
  198.     char    *names;
  199. {
  200.  
  201. # if VMS
  202. /* 
  203.    We could use expand() [vmswild.c], but what's the point on VMS? 
  204.    Anyway, echo is the wrong thing to do, it takes too long to build
  205.    a subprocess on VMS and any "echo" program would have to be supplied
  206.    by elvis.  More importantly, many VMS utilities expand names 
  207.    themselves (the shell doesn't do any expansion) so the concept is
  208.    non-native.  jdc
  209. */
  210.     return names;
  211. # else
  212.  
  213.     int    i, j, fd;
  214.     REG char *s, *d;
  215.  
  216.  
  217.     /* build the echo command */
  218.     if (names != tmpblk.c)
  219.     {
  220.         /* the names aren't in tmpblk.c, so we can do it the easy way */
  221.         strcpy(tmpblk.c, PROG);
  222.         strcat(tmpblk.c, names);
  223.     }
  224.     else
  225.     {
  226.         /* the names are already in tmpblk.c, so shift them to make
  227.          * room for the word "echo "
  228.          */
  229.         for (s = names + strlen(names) + 1, d = s + PROGLEN; s > names; )
  230.         {
  231.             *--d = *--s;
  232.         }
  233.         strncpy(names, PROG, PROGLEN);
  234.     }
  235.  
  236.     /* run the command & read the resulting names */
  237.     fd = rpipe(tmpblk.c, 0);
  238.     if (fd < 0) return names;
  239.     i = 0;
  240.     do
  241.     {
  242.         j = tread(fd, tmpblk.c + i, BLKSIZE - i);
  243.         i += j;
  244.     } while (j > 0);
  245.  
  246.     /* successful? */
  247.     if (rpclose(fd) == 0 && j == 0 && i < BLKSIZE && i > 0)
  248.     {
  249.         tmpblk.c[i-1] = '\0'; /* "i-1" so we clip off the newline */
  250.         return tmpblk.c;
  251.     }
  252.     else
  253.     {
  254.         return names;
  255.     }
  256. # endif
  257. }
  258. #endif
  259.  
  260. /* This function runs a range of lines through a filter program, and replaces
  261.  * the original text with the filtered version.  As a special case, if "to"
  262.  * is MARK_UNSET, then it runs the filter program with stdin coming from
  263.  * /dev/null, and inserts any output lines.
  264.  */
  265. int filter(from, to, cmd, back)
  266.     MARK    from, to;    /* the range of lines to filter */
  267.     char    *cmd;        /* the filter command */
  268.     int    back;        /* boolean: will we read lines back? */
  269. {
  270.     int    scratch;    /* fd of the scratch file */
  271.     int    fd;        /* fd of the pipe from the filter */
  272.     char    scrout[50];    /* name of the scratch out file */
  273.     MARK    new;        /* place where new text should go */
  274.     long    sent, rcvd;    /* number of lines sent/received */
  275.     int    i, j;
  276.  
  277.     /* write the lines (if specified) to a temp file */
  278.     if (to)
  279.     {
  280.         /* we have lines */
  281. #if MSDOS || TOS
  282.         strcpy(scrout, o_directory);
  283.         if ((i=strlen(scrout)) && !strchr("\\/:", scrout[i-1]))
  284.             scrout[i++]=SLASH;
  285.         strcpy(scrout+i, SCRATCHOUT+3);
  286. #else
  287.         sprintf(scrout, SCRATCHOUT, o_directory);
  288. #endif
  289.         mktemp(scrout);
  290.         cmd_write(from, to, CMD_BANG, FALSE, scrout);
  291.         sent = markline(to) - markline(from) + 1L;
  292.  
  293.         /* use those lines as stdin */
  294.         scratch = open(scrout, O_RDONLY);
  295.         if (scratch < 0)
  296.         {
  297.             unlink(scrout);
  298.             return -1;
  299.         }
  300.     }
  301.     else
  302.     {
  303.         scratch = 0;
  304.         sent = 0L;
  305.     }
  306.  
  307.     /* start the filter program */
  308. #if VMS
  309.     /* 
  310.        VMS doesn't know a thing about file descriptor 0.  The rpipe
  311.        concept is non-portable.  Hence we need a file name argument.
  312.     */
  313.     fd = rpipe(cmd, scratch, scrout);
  314. #else
  315.     fd = rpipe(cmd, scratch);
  316. #endif
  317.     if (fd < 0)
  318.     {
  319.         if (to)
  320.         {
  321.             close(scratch);
  322.             unlink(scrout);
  323.         }
  324.         return -1;
  325.     }
  326.  
  327.     if (back)
  328.     {
  329.         ChangeText
  330.         {
  331.             /* adjust MARKs for whole lines, and set "new" */
  332.             from &= ~(BLKSIZE - 1);
  333.             if (to)
  334.             {
  335.                 to &= ~(BLKSIZE - 1);
  336.                 to += BLKSIZE;
  337.                 new = to;
  338.             }
  339.             else
  340.             {
  341.                 new = from + BLKSIZE;
  342.             }
  343.  
  344. #if VMS
  345. /* Reading from a VMS mailbox (pipe) is record oriented... */
  346. # define tread vms_pread
  347. #endif
  348.  
  349.             /* repeatedly read in new text and add it */
  350.             rcvd = 0L;
  351.             while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0)
  352.             {
  353.                 tmpblk.c[i] = '\0';
  354.                 add(new, tmpblk.c);
  355. #if VMS
  356.                 /* What!  An advantage to record oriented reads? */
  357.                 new += (i - 1);
  358.                 new = (new & ~(BLKSIZE - 1)) + BLKSIZE;
  359.                 rcvd++;
  360. #else
  361.                 for (i = 0; tmpblk.c[i]; i++)
  362.                 {
  363.                     if (tmpblk